package com.example.ass7.sprite;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import biuoop.DrawSurface;
import biuoop.KeyboardSensor;

import com.example.ass7.collision.Collidable;
import com.example.ass7.collision.HitListener;
import com.example.ass7.geometryprimitives.Point;
import com.example.ass7.geometryprimitives.Rectangle;
import com.example.ass7.geometryprimitives.Velocity;
import com.example.ass7.level.GameLevel;

/**
 * @author Meirav Ben Izhak
 */
public class Paddle implements Sprite, Collidable {
    // members;
    private final biuoop.KeyboardSensor keyboard;
    private Rectangle rectangle;
    // private List<Rectangle> regions;
    private final double speedMove;
    private final List<HitListener> hitListeners;

    /**
     * @param keyboardSensor
     *            keyboardSensor
     * @param center
     *            center
     * @param width
     *            width
     * @param speedMove
     *            speedMove
     */
    public Paddle(
        KeyboardSensor keyboardSensor, Point center,
        double width, int speedMove) {
        this.rectangle = new Rectangle(
                center, width, 50);
        this.keyboard = keyboardSensor;
        this.speedMove = speedMove;
        // this.regions = new ArrayList<Rectangle>();
        // this.regions = this.setPaddleRegions();
        this.hitListeners = new ArrayList<HitListener>();
    }

    /**
     * @param center
     *            center
     * @param width
     *            width
     */
    public void setPosition(Point center,
            double width) {
        this.rectangle = new Rectangle(
                center, width, 17);
    }

    /**
     * @return Point
     */
    public Point getCenterTopPosition() {
        Point point = new Point(
                rectangle.getxLowest() + (rectangle.getWidth() / 2),
                rectangle.getyLowest() - 2);
        return point;
    }

    /**
     * @param dt
     *            dt
     */
    public void moveRight(double dt) {
        double nextX = this.rectangle
                .getUpperLeft().getX()
                + (this.speedMove * dt);
        double nextY = this.rectangle
                .getUpperLeft().getY();
        if (nextX
                + this.rectangle
                        .getWidth() > 800) {
            nextX = 800 - this.rectangle
                    .getWidth();
        }
        Point moveTo = new Point(
                nextX, nextY);
        this.rectangle = new Rectangle(
                moveTo, this.rectangle.getWidth(), this.rectangle.getHeight());
    }

    /**
     * @param dt
     *            dt
     */
    public void moveLeft(double dt) {
        double nextX = this.rectangle
                .getUpperLeft().getX()
                - (this.speedMove * dt);
        double nextY = this.rectangle
                .getUpperLeft().getY();
        if (nextX < 0) {
            nextX = 0;
        }
        Point moveTo = new Point(
                nextX, nextY);
        this.rectangle = new Rectangle(
                moveTo, this.rectangle.getWidth(), this.rectangle.getHeight());
    }

    // Sprite
    @Override
    public void timePassed(double dt) {
        if (this.keyboard
                .isPressed(KeyboardSensor.LEFT_KEY)) {
            this.moveLeft(dt);
        }
        if (this.keyboard
                .isPressed(KeyboardSensor.RIGHT_KEY)) {
            this.moveRight(dt);
        }
    }

    @Override
    public void drawOn(DrawSurface surface) {
        int x1 = (int) (this.rectangle
                .getUpperLeft()
                .getX());
        int y1 = (int) (this.rectangle
                .getUpperLeft()
                .getY());
        int width = (int) (this.rectangle
                .getWidth());
        int height = (int) (this.rectangle
                .getHeight());
        surface.setColor(Color.ORANGE);
        surface.fillRectangle(x1,
                y1,
                width,
                height);
    }

    /**
     * @param hitter
     *            hitter
     */
    private void notifyHit(Ball hitter) {
        // Make a copy of the hitListeners before iterating over them.
        List<HitListener> listeners = new ArrayList<HitListener>(
                this.hitListeners);
        // Notify all listeners about a hit event:
        for (HitListener hl : listeners) {
            hl.hitEvent(null, this,
                    hitter);
        }
    }

    // Collidable
    @Override
    public Rectangle getCollisionRectangle() {
        return this.rectangle;
    }

    @Override
    public Velocity hit(Ball hitter,
            Point collisionPoint,
            Velocity currentVelocity) {

        Velocity toReturn = currentVelocity;
        // int whichRegion = 0;
        double currentSpeed = currentVelocity
                .getSpeed();
        double paddleLeftBorder = this
                .getCollisionRectangle().getxLowest();
        double paddleRightBorder = this
                .getCollisionRectangle().getxHighest();
        double paddleUpBorder = this
                .getCollisionRectangle().getyLowest();
        double paddleDownBorder = this
                .getCollisionRectangle().getyHighest();
        double collisionX = collisionPoint
                .getX();
        // double collisionY = collisionPoint.getY();
        if (paddleRightBorder == collisionX
                || paddleRightBorder == collisionX
                || (paddleLeftBorder <= hitter
                        .getX()
                        && hitter
                                .getX() <= paddleRightBorder
                        && paddleUpBorder <= hitter
                                .getY() && hitter
                        .getY() <= paddleDownBorder)) {
            toReturn = new Velocity(
                    -currentVelocity.getDx(), currentVelocity.getDy());
        } else {
            toReturn = new Velocity(
                    currentVelocity.getDx(), -currentVelocity.getDy());

            double width = this.rectangle
                    .getWidth();
            double widthPerPart = width / 5;
            for (int indexOfParts = 0; indexOfParts < 5; indexOfParts++) {
                double leftOfPart = paddleLeftBorder
                        + widthPerPart * indexOfParts;
                double rightOfPart = paddleLeftBorder
                        + widthPerPart * indexOfParts + widthPerPart;
                if (indexOfParts == 0
                        && leftOfPart <= collisionX
                        && collisionX < rightOfPart) {
                    toReturn = Velocity
                            .fromAngleAndSpeed(-60,
                                    currentSpeed);
                    toReturn.setDy(-Math
                            .abs(toReturn
                                    .getDy()));
                    break;

                } else if (indexOfParts == 1
                        && leftOfPart <= collisionX
                        && collisionX < rightOfPart) {
                    toReturn = Velocity
                            .fromAngleAndSpeed(-30,
                                    currentSpeed);
                    toReturn.setDy(-Math
                            .abs(toReturn
                                    .getDy()));
                    break;

                } else if (indexOfParts == 2
                        && leftOfPart <= collisionX
                        && collisionX < rightOfPart) {
                    toReturn = new Velocity(
                            currentVelocity.getDx(), -currentVelocity.getDy());
                    toReturn.setDy(-Math
                            .abs(toReturn
                                    .getDy()));
                    break;

                } else if (indexOfParts == 3
                        && leftOfPart <= collisionX
                        && collisionX < rightOfPart) {
                    toReturn = Velocity
                            .fromAngleAndSpeed(-330,
                                    currentSpeed);
                    toReturn.setDy(-Math
                            .abs(toReturn
                                    .getDy()));
                    break;

                } else if (indexOfParts == 4
                        && leftOfPart <= collisionX
                        && collisionX < rightOfPart) {
                    toReturn = Velocity
                            .fromAngleAndSpeed(-300,
                                    currentSpeed);
                    toReturn.setDy(-Math
                            .abs(toReturn
                                    .getDy()));
                    break;
                }
            }
        }
        notifyHit(hitter);
        return toReturn;
    }

    // Add this paddle to the game.
    /**
     * @param g
     *            g
     */
    public void addToGame(GameLevel g) {
        g.addSprite(this);
        g.addCollidable(this);
    }

    @Override
    public void addHitListener(HitListener hl) {
        this.hitListeners.add(hl);
    }

    @Override
    public void removeHitListener(HitListener hl) {
        this.hitListeners.remove(hl);
    }

}
